#ifndef unigame_Camera_h
#define unigame_Camera_h

#include "GameObject.h"
#include "common/Pool.h"
#include "config/MathDef.h"

#undef far
#undef near

namespace unigame
{
	struct RenderObj
	{
		GameObject* m_obj;
		RenderObj* m_next;
		
		union FloatInfinity
		{
			float z;
			uint32u minusInfinity;
		} m_z;

		RenderObj():m_obj(0), m_next(0)
		{
			m_z.minusInfinity = MINUS_INFINITY;
		}
	};

	class Display;
	class Camera: public GameObject
	{
	public:
		virtual ~Camera();

		static Camera* create(const char* name)
		{
			return uni_new Camera(name);
		}

		void far(float far)
		{
			m_isProjectDirty = true;
			m_far = far;
		}

		float far()
		{
			return m_far;
		}

		void near(float near)
		{
			m_isProjectDirty = true;
			m_near = near;
		}

		float near()
		{
			return m_near;
		}

		float size()
		{
			return m_size;
		}

		void size(float size)
		{
			if(m_isOrthographic)
			{
				m_isProjectDirty = true;
			}
			m_size = size;
		}

		float fovInAngle()
		{
			return m_fov;
		}

		void fovInAngle(float fov)
		{
			if(!m_isOrthographic)
			{
				m_isProjectDirty = true;
			}
			m_fov = fov;
		}

		void aspect(float aspect)
		{
			m_isProjectDirty = true;
			m_aspect = aspect;
		}

		float aspect()
		{
			return m_aspect;
		}

		bool isOrthographic()
		{
			return m_isOrthographic;
		}

		void isOrthographic(bool isOrth)
		{
			if(m_isOrthographic != isOrth)
			{
				m_isProjectDirty = true;
				m_isOrthographic = isOrth;
			}
		}

		void update();

		Matrixf& getCameraToWrold()
		{
			return m_cameraToWorld;
		}

		Matrixf& getWorldToCamera()
		{
			return m_worldToCamera;
		}

		Matrix4x4f& getProjectMatrix()
		{
			return m_projectMatrix;
		}

		void cullObjects();

		void display(Display* dis);

		void display(int32u disID);

		Display* display()
		{
			return m_display;
		}

		void render();

	private:
		Camera(const char* name);
		Camera(const Camera& camera);
		Camera& operator =(const Camera& camera);

		void recalculateView();
		void recalculateProject();

		void addToOpaque(GameObject* obj, int32u order);
		void addToTransparent(GameObject* obj, int32u order);
		void renderOpaque();
		void renderTransparent();

	private:
		Matrixf m_cameraToWorld;
		Matrixf m_worldToCamera;
		Matrix4x4f m_projectMatrix;

		RenderObj* m_opaqueList;
		RenderObj* m_transparentList;

		Pool<RenderObj> m_renderPool;

		float m_fov;
		float m_near;
		float m_far;
		float m_aspect;
		int32u m_mask;
		float m_size;
		Display* m_display;
	
		bool m_isProjectDirty;
		bool m_isOrthographic;
	};
}

#endif